using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Galplay {
  public enum GameSceneState {
    Ready, // 所有的脚本都已经执行完毕，等待用户点击。
    Play, // 正在按顺序播放脚本
  }

  /// <summary>
  /// 这里为什么是双重机制。是这样的，比如在Wolf中，显示上一条文字之后，他会有一些动作，比如说角色移动什么的。然后这中间有很多GAP。
  /// 这时候如果按WaitFrame去播放，那么要等很长的时间才会轮到文字显示。所以文字必须单独提出来，使用Click就能从一个点击到下一个。
  /// 而其它的使用另外一个序列去Play。
  /// </summary>
  public enum GameTextState {
    Ready,
    ShowText,
  }

  public class GameScenePlayer : MonoBehaviour {
    // 用于显示文本的控件
    public Text main_text;
    public Text main_text_shadow;
    [Tooltip("显示背景的图片")]
    public Image backgroundImage;
    [Tooltip("Skip每执行一个Break断点需要的时间")]
    public float skipInterval = 0.1f;
    public float skipFlashRate = 0.05f;
    [Tooltip("自动播放的时候，每一个文字的等待间隔")]
    public float autoTimePerChar = 0.04f;
    [Tooltip("文字播放的速度")]
    public float timePerChar = 0.03f;
    [Tooltip("用于Auto模式的标识")]
    public Image autoModeSymbol;
    [Tooltip("用于Skip模式的标识")]
    public Image skipModeSymbol;
    [Tooltip("用于NoWait模式的标识")]
    public Image noWaitModeSymbol;
    [Tooltip("用于速度模式的标识")]
    public TMP_Text speedText;
    [Tooltip("图片管理器")]
    public ImageManager imageManager;


    public GalScene Scene { get; private set; }
    public GalScriptPack Events { get; private set; }
    public GameSceneState State { get; private set; }
    public GameTextState TextState { get; private set; }
    public GalScript Current { get; private set; }
    public PlayQueue playQueue { get; private set; }

    private GameManager GM { get => GameManager.Instance; }
    // 是否是无等待模式
    public bool IsNoWait { get; private set; } = false;
    // 是否启动了SKIP
    public bool IsSkipMode { get; private set; }
    // 上一次执行Skip的时间剩余
    private float skip_time_last = 0;
    // 是否启动了自动播放
    public bool IsAutoMode { get; private set; }
    // 自动播放的剩余时间
    public float auto_time_last = 0;
    // 在播放文字的时候设置为0，根据总量计算应该播放的文字。
    public float text_time_tick = 0;
    // 需要等待的Frame，如果此Frame大于0，则需要等待这个倒计时结束之后，再执行一下条
    public float wait_time = 0.0f;

    public static GameScenePlayer Instance { get; private set; }

    private float deltaTime;

    public float AutoTimePerChar { get => autoTimePerChar / GM.PlaySpeed; }
    public float TimePerChar { get => timePerChar / GM.PlaySpeed; }

    public string ShowText { get; private set; }
    public int TextIndex { get; private set; }

    void Awake() {
      Instance = this;
      playQueue = new PlayQueue(this);
    }

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start() {
      SetState(GameSceneState.Ready);

      UiUtils.AddUIListener(main_text.gameObject, OnClick);
      UiUtils.AddUIListener(backgroundImage.gameObject, OnClick);
    }


    // Update is called once per frame
    void Update() {

    }

    void FixedUpdate() {
      deltaTime = Time.fixedDeltaTime;

      if (State == GameSceneState.Ready) {
        OnReady();
      } else if (State == GameSceneState.Play) {
        OnPlay();
      }

      if (TextState == GameTextState.Ready) {
        CheckAuto();
        CheckSkip();
      } else if (TextState == GameTextState.ShowText) {
        OnShowText();
      }

    }

    private void OnPlay() {
      // 如果是Skip模式，忽略
      if (!IsSkipMode) {
        // 检查等待时间
        if (wait_time > 0.0f) {
          wait_time -= deltaTime;
          if (wait_time > 0.0f) {
            return;
          }
        }
      }

      // 从队列中提取项目依次执行
      while (true) {
        var script = playQueue.Pop();
        if (script == null) {
          // 这里不需要改变状态，因为必然有一个脚本去改变状态
          return;
        }

        // 执行脚本
        RunScript(script);

        if (!IsSkipMode) { // Skip模式直接跳过
          // 如果触发了等待，则跳出循环
          if (wait_time > 0.0f) {
            return;
          }
        }
      }



    }

    // 这是一个用于Skip的特殊处理，就是控制Picture不一直显示在前面
    private int SkipClearCount { get; set; }

    private void CheckSkip() {
      // 如果处于Skip模式，则进行Skip
      if (IsSkipMode) {
        skip_time_last -= deltaTime;
        if (skip_time_last < 0) {
          skip_time_last += skipInterval;
          if (SkipClearCount++ > 20) {
            ClearState();
            SkipClearCount = 0;
          }
          RunToReady();
        } else {
          return;
        }
      }
    }

    private void CheckAuto() {
      if (IsAutoMode) {
        auto_time_last -= deltaTime;
        if (auto_time_last < 0) {
          auto_time_last = 0;
          RunToReady();
        } else {
          return;
        }
      }
    }

    private void OnReady() {
      CheckSkip();
      CheckAuto();
    }

    private void OnShowText() {
      // 计算时间
      text_time_tick += deltaTime;

      if (!IsSkipMode) {
        // 根据时间计算Count
        var current_index = Math.Min((int)(text_time_tick / TimePerChar), ShowText.Length - 1);
        if (TextIndex != current_index) {
          TextIndex = current_index;
          SetText(ShowText[..TextIndex]);
        }
        // 显示完了就退出ShowMode
        if (TextIndex == ShowText.Length - 1) {
          SetTextState(GameTextState.Ready);
        }
      }

      CheckSkip();
      CheckAuto();
    }

    // 处理点击命令，执行下一行
    public void OnClick() {
      // 如果还没有载入，则跳过
      if (Scene == null) { return; }

      SetSkipMode(false);
      SetAutoMode(false);

      // 如果处于ShowMode，则清空文字
      if (TextState == GameTextState.ShowText) {
        playQueue.Flush();
        FlushText();
        SetTextState(GameTextState.Ready);
      } else {
        RunToReady();
      }


    }

    public void SetSkipMode(bool enable) {
      IsSkipMode = enable;
      skipModeSymbol.gameObject.SetActive(enable);
    }

    public void SetAutoMode(bool enable) {
      IsAutoMode = enable;
      autoModeSymbol.gameObject.SetActive(enable);
    }

    public void SetNoWait(bool enable) {
      IsNoWait = enable;
      noWaitModeSymbol.gameObject.SetActive(enable);
    }

    public void ToggleSkip() {
      SetSkipMode(!IsSkipMode);
      SetAutoMode(false);
    }

    public void ToggleAuto() {
      SetAutoMode(!IsAutoMode);
      SetSkipMode(false);
    }

    public void ToggleNoWait() {
      SetNoWait(!IsNoWait);
    }

    public void SetWaitTime(float time) {
      wait_time = time;
    }

    static readonly float[] speed_list = new float[] { 1.0f, 1.5f, 2.0f, 3.0f, 4.0f, 5.0f };
    private const float Tolerance = 0.1f; // 浮点数比较容差

    public void ToggleSpeed() {
      float current_speed = GM.PlaySpeed;

      // 查找当前速度在数组中的位置(使用近似比较)
      int foundIndex = -1;
      for (int i = 0; i < speed_list.Length; i++) {
        if (Math.Abs(speed_list[i] - current_speed) < Tolerance) {
          foundIndex = i;
          break;
        }
      }

      // 计算下一个速度的索引
      int nextIndex;
      if (foundIndex >= 0) {
        // 如果找到当前速度，取下一个
        nextIndex = (foundIndex + 1) % speed_list.Length;
      } else {
        // 如果当前速度不在列表中，使用第一个速度
        nextIndex = 0;
      }

      GM.PlaySpeed = speed_list[nextIndex];

      speedText.text = GM.PlaySpeed.ToString("F1");
    }

    // 执行脚本，直到下一个等待项目
    void RunToReady() {
      // 如果已经到了结束，则跳出
      if (playQueue.IsEnd) {
        return;
      }

      // 如果还拥有脚本，执行完
      playQueue.Flush();

      // 汲取Script
      playQueue.PickScript();
      // 设置为Play模式
      SetState(GameSceneState.Play);

      // 设置Process
      GM.SetProcessOnly(playQueue.NextIndex / (float)Scene.Scripts.Count);
    }


    public void RunScript(GalScript script) {
      if (script == null) { return; }
      // 设置当前的脚本运行
      Current = script;
      // 执行脚本
      script.Execute(this);
    }


    // 获得下一个脚本，如果当前脚本为空，
    GalScript GetNextScript() {
      // 如果当前脚本不存在，则返回第一行。
      if (Current == null) {
        return Scene.GetLine(0);
      }
      // 如果当前脚本存在，返回下一个
      return Scene.GetLine(Current.Index + 1);
    }

    void SetState(GameSceneState state) {
      State = state;
    }

    void SetTextState(GameTextState state) {
      TextState = state;
    }

    void ClearResources() {
      GameResources.Instance.ClearAll();
    }

    public void Play(GameScene scene) {
        // 清空资源
        ClearResources();

        var game = scene.Owner;
        this.Scene = new GalScene(scene.Name, new XmlReader(scene.Content));
        imageManager.SetResolution(new Vector2(scene.Owner.Width, scene.Owner.Height));

        // 如果没有Event，则不载入。Events不是必须的。
        if (Kit.NotEmpty(game.EventPath)) {
          this.Events = new GalScriptPack("Event", new XmlReader(game.GetMaster(game.EventPath)));
        } else {
          this.Events = null;
        }


        // 初始化状态
        SetState(GameSceneState.Ready);
        this.Current = null;
        this.playQueue.ResetStart();

        // 执行到下一个需要等待的项目
        this.RunToReady();

        GM.UseValueChanged = true;

    }

    internal void SetShowText(string text) {
      // 需要设置自动时间
      SetAutoTime(AutoTimePerChar * text.Length);
      // 设置准备
      ShowText = text;
      TextIndex = 0;

      // 根据是否是SkipMode，做不同的处理。
      if (IsSkipMode) {
        FlushText();
      } else {
        SetText("");
        text_time_tick = 0;
      }

      // 设置为ShowText
      if (IsNoWait) {
        FlushText();
        SetTextState(GameTextState.Ready);
      } else {
        SetTextState(GameTextState.ShowText);
      }

    }

    public void FlushText() {
      TextIndex = ShowText.Length - 1;
      SetText(ShowText);
    }

    public void SetAutoTime(float time) {
      auto_time_last = time;
    }


    void SetText(string text) {
      main_text.text = text;
      main_text_shadow.text = text;
    }

    public void SetProcess(float process) {

      imageManager.HideAll();
      playQueue.Jump(process);
      RunToReady();
    }

    internal void PlayText(ScriptText script) {
      var text = script.GetText();
      // 设置显示文本
      SetShowText(text);

      // 播放声音，这个需要在文本后面，会覆盖掉文本的声音播放。
      if (script.Voice != 0) {
        GM.PlayVoice(script.Voice);
      }
      if (Kit.NotEmpty(script.VoiceFile)) {
        GM.PlayVoice(script.GetVoiceFile());
      }
    }

    // 当脚本出现问题的时候，使用这个清除
    // 重置除背景以外的图片，关闭BGS。
    public void ClearState() {
      imageManager.HideAll();
      GM.StopBgs();
    }
  }
}